package com.vip.admin.common.security;

import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.Cache.ValueWrapper;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.stereotype.Component;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.vip.admin.common.constant.CommonConstant;
import com.vip.admin.common.utils.IPUtils;
import com.vip.admin.common.utils.JwtUtil;
import com.vip.admin.common.utils.SpringContextUtils;
import com.vip.admin.modules.system.entity.SysUser;
import com.vip.admin.modules.system.service.ISysResourcesService;
import com.vip.admin.modules.system.service.ISysRoleService;
import com.vip.admin.modules.system.service.ISysUserService;

import lombok.extern.log4j.Log4j2;

@Component
@Log4j2
public class ShiroRealm extends AuthorizingRealm {

	@Autowired
	ISysUserService sysUserService;
	@Autowired
	ISysRoleService sysRoleService;
	@Autowired
	ISysResourcesService sysResourcesService;
	@Autowired
	EhCacheCacheManager ehcacheCacheManager;

	@Override
	public boolean supports(AuthenticationToken token) {
		return token instanceof JwtToken;
	}

	/**
	 * 身份认证
	 **/
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
		String token = (String) auth.getCredentials();
		if (token == null) {
			log.info("————————身份认证失败——————————IP地址:{}",
					IPUtils.getIpAddrByRequest(SpringContextUtils.getHttpServletRequest()));
			throw new AuthenticationException("token为空!");
		}
		// 校验token有效性
		SysUser sysUser = this.checkUserTokenIsEffect(token);
		return new SimpleAuthenticationInfo(sysUser, token, getName());
	}

	private SysUser checkUserTokenIsEffect(String token) {
		String username = JwtUtil.getUsername(token);
		if (StringUtils.isBlank(username)) {
			throw new AuthenticationException("token非法无效!");
		}
		// 查询用户信息
		log.info("———校验token是否有效————checkUserTokenIsEffect——————— " + token);
		QueryWrapper<SysUser> queryWrapper = new QueryWrapper<SysUser>();
		queryWrapper.eq("USER_NAME", username);
		SysUser sysUser = sysUserService.getOne(queryWrapper);
		if (sysUser == null) {
			throw new AuthenticationException("用户不存在!");
		}
		// 判断用户状态
		if (sysUser.getStatus() != CommonConstant.USER_UNFREEZE) {
			throw new AuthenticationException("账号已被锁定,请联系管理员!");
		}
		// 校验token是否超时失效 & 或者账号密码是否错误
		if (!jwtTokenRefresh(token, username, sysUser.getPassword())) {
			throw new AuthenticationException("Token失效，请重新登录!");
		}
		return sysUser;
	}

	private boolean jwtTokenRefresh(String token, String userName, String passWord) {
		Cache cache = ehcacheCacheManager.getCache("userSession");
		ValueWrapper tokenVObj = cache.get(token);
		if (tokenVObj != null && StringUtils.isNotBlank(tokenVObj.toString())) {
			String cacheToken = tokenVObj.toString();
			if (!JwtUtil.verify(cacheToken, userName, passWord)) {
				String newToken = JwtUtil.sign(userName, passWord);
				cache.put(cacheToken, newToken);
				log.info("——————————用户在线操作，更新token保证不掉线—————————jwtTokenRefresh——————— " + token);
			}
			return true;
		}
		return false;
	}

	/**
	 * 权限认证
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		String username = null;
		if (principals != null) {
			SysUser sysUser = (SysUser) principals.getPrimaryPrincipal();
			username = sysUser.getUserName();
		}
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		Set<String> roleCodes = sysRoleService.getUserRolesSet(username);
		info.setRoles(roleCodes);
		Set<String> pars = sysResourcesService.getSysResourcesByUsername(username);
		info.addStringPermissions(pars);
		return info;
	}

}
